﻿using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Xml;
using log4net;
using System;

namespace Updater.Common
{
    public enum DownloadTimeType
    {
        EveryDay,
        EveryWeek,
        EveryMonth,
        EveryYear,
        OnlyWhenForced
    }

    public class SiteInfo
    {

        private static readonly ILog log = LogManager.GetLogger(typeof(SiteInfo));

        #region Fields and properties

        string id;
        public string Id
        {
            get { return id; }
        }

        string directoryToUpdate;
        public string DirectoryToUpdate
        {
            get { return directoryToUpdate; }
        }

        string applicationName;
        public string ApplicationName
        {
            get { return applicationName; }
        }

        string downloadFileExtension;
        public string DownloadFileExtension
        {
            get { return downloadFileExtension; }
        }

        bool forceInstall;
        public bool ForceInstall
        {
            get { return forceInstall; }
        }

        DateTime downloadTimeAndDay;
        public DateTime DownLoadTimeAndDay
        {
            get { return downloadTimeAndDay; }
        }

        // TODO: this may be replaced with a call to the MSI database for date of last update
        DateTime lastUpdate;
        public DateTime LastUpdate
        {
            get { return lastUpdate; }
        }

        DayOfWeek downloadDayOfWeek;
        public DayOfWeek DownloadDayOfWeek
        {
            get { return downloadDayOfWeek; }
        }

        DownloadTimeType downloadTimeType;
        public DownloadTimeType DownLoadTimeType
        {
            get { return downloadTimeType; }
        }
        
        // TODO: searches directoryToUpdate or the MSI database and finds its versionnumber. If no version exists, it should return 0.0.0.0.
        private Version versionNumber;
        public Version VersionNumber
        {
            get
            {
                if (versionNumber == null)
                {
                    versionNumber = new Version(0, 0, 0, 0);
                }
                return versionNumber;
            }
            set
            {
                versionNumber = value;
            }
        }
        
        public string ApplicationNameWithExtension
        {
            get
            {
                return applicationName + downloadFileExtension;
            }            
        }

        public Dictionary<string, string> Parameters { get; set; }

        #endregion

        // Read SiteInfo from XmlNode in document
        public SiteInfo(XmlNode siteNode)
        {
            try
            {        
                XmlAttributeCollection attributes = siteNode.Attributes;

                id = attributes["id"].Value;
                directoryToUpdate = attributes["directoryToUpdate"].Value;
                applicationName = attributes["applicationName"].Value;
                downloadFileExtension = attributes["downloadFileExtension"].Value;
                downloadTimeType = (DownloadTimeType)Enum.Parse(typeof(DownloadTimeType), attributes["downloadTimeType"].Value);
                forceInstall = bool.Parse(attributes["forceUpdate"].Value);
                string currentVersion = attributes["currentVersion"].Value;
                VersionNumber = new Version(currentVersion);

                string timeAttributeValue;
                timeAttributeValue = attributes["lastUpdate"].Value;
                if (timeAttributeValue != "")
                    lastUpdate = DateTime.Parse(attributes["lastUpdate"].Value);
                else
                    lastUpdate = new DateTime(1000, 1, 1, 0, 0, 0); // default for no update done yet
                downloadTimeAndDay = new DateTime(1000, 1, 1, 0, 0, 0);                
                timeAttributeValue = attributes["hourOfDayToInstall"].Value;
                if (timeAttributeValue != "")
                    downloadTimeAndDay = downloadTimeAndDay.AddHours(double.Parse(timeAttributeValue));
                timeAttributeValue = attributes["dayOfMonth"].Value;
                if (timeAttributeValue != "")
                    downloadTimeAndDay = downloadTimeAndDay.AddDays(double.Parse(timeAttributeValue) - 1);
                timeAttributeValue = attributes["month"].Value;
                if (timeAttributeValue != "")
                    downloadTimeAndDay = downloadTimeAndDay.AddMonths(int.Parse(timeAttributeValue) - 1);
                timeAttributeValue = attributes["dayOfWeek"].Value;
                if (timeAttributeValue != "")
                    downloadDayOfWeek = (DayOfWeek)int.Parse(timeAttributeValue);

                Parameters = new Dictionary<string, string>();
                foreach (XmlNode param in siteNode.SelectNodes("Param"))
                {
                    attributes = param.Attributes;
                    string name = attributes["name"].Value;
                    string value = attributes["value"].Value;
                    Parameters.Add(name, value);
                }
            }
            catch (Exception ex)
            {
                string ErrorMessage = "Error during reading of SiteInfo " + directoryToUpdate + " from UpdaterSettings.xml";
                log.Error(ErrorMessage);
                throw( new Exception( ErrorMessage, ex ));
            }
        }

        /// <summary>
        /// Saves version number to xml node.
        /// </summary>
        /// <param name="siteNode">Xml node.</param>
        public void SaveCurrentVersionValue(XmlNode siteNode)
        {
            siteNode.Attributes["currentVersion"].Value = VersionNumber.ToString();
        }

        /// <summary>
        ///  Checks, if the file suffix of the proposed download-version(e.g. _BETA) is acceptable
        /// </summary>
        /// <param name="newVersion">The proposed download-version</param>
        /// <returns>Site accepts suffix</returns>
        public bool AcceptsVersionType(string newVersion) // TODO: might need revision
        {
            bool answer = true;

            string[] words = newVersion.Split('_');
            if (words.Length > 1)
            {
                string versionExtension = "_" + words[words.Length - 1].Split('.')[ 0 ]; // the last element
                answer = (downloadFileExtension == versionExtension);
            }

            return answer;
        }

        /// <summary>
        /// Returns if the time has come for the site to install a downloaded file.
        /// </summary>
        /// <param name="dateTime">Time to install</param>
        /// <returns>Site wants to install</returns>
        public bool IsTimeToInstall(DateTime dateTime)
        {
            bool answer = false;

            if (forceInstall)
                answer = true;
            else if( dateTime.Hour == downloadTimeAndDay.Hour ) // install only on hourOfDayToInstall, if forceInstall == False
            {
                switch (downloadTimeType)
                {
                    case DownloadTimeType.EveryDay:
                        answer = true;
                        break;
                    case DownloadTimeType.EveryWeek: // download only on selected day of week, if more than 6 days has passed since last update, or if more than two weeks has passed since last update
                        if (dateTime.DayOfWeek == DownloadDayOfWeek && (dateTime - LastUpdate) > new TimeSpan(6, 0, 0, 0))
                            answer = true;
                        else if ((dateTime - LastUpdate) > new TimeSpan(14, 0, 0, 0))
                            answer = true;
                        break;
                    case DownloadTimeType.EveryMonth: // download on selected day of month, or if more than two months has passed since last update
                        if (dateTime.Day == DownLoadTimeAndDay.Day && (dateTime - LastUpdate) > new TimeSpan(6, 0, 0, 0))
                            answer = true;
                        else if ((dateTime - LastUpdate) > new TimeSpan(62, 0, 0, 0))
                            answer = true;
                        break;
                    case DownloadTimeType.EveryYear: // download on selected day of year, or if more than a year has passed since last update
                        if (dateTime.Date == DownLoadTimeAndDay.Date && (dateTime - LastUpdate) > new TimeSpan(6, 0, 0, 0))
                            answer = true;
                        else if ((dateTime - LastUpdate) > new TimeSpan(366, 0, 0, 0))
                            answer = true;
                        break;
                }                
            }

            return answer;
        }

        /// <summary>
        /// Notifies end-users about updating in progress.
        /// </summary>
        public void NotifyUser() // TODO: Write method to notify users about updating in progress
        {
        }

        /// <summary>
        /// Installs installationFile
        /// </summary>
        /// <param name="installationFile">File to install in directoryToUpdate</param>
        /// <param name="downloadPath">Path of the installationFile</param>
        /// <returns>Installation succeeded</returns>
        public bool DoInstall(string installationFile,string downloadPath) // TODO: write method
        {
            MSIInstaller installer = new MSIInstaller();
            int result = installer.InstallMsiTo(System.IO.Path.Combine(downloadPath, installationFile), directoryToUpdate, Parameters);
            return result == 0;
        }
 
    }
}